package br.com.thor.biblioteca.mensageria.servico;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import br.com.thor.biblioteca.mensageria.config.Ativador;
import br.com.thor.biblioteca.mensageria.config.ResolutorTipo;
import br.com.thor.biblioteca.mensageria.mensagem.MensagemRequestBase;
import br.com.thor.biblioteca.mensageria.mensagem.MensagemResponseAnotacao;
import br.com.thor.biblioteca.mensageria.mensagem.MensagemResponseBase;

public class ServicoMensageria implements IServicoMensageria {

	// Dicionário relacionando tipo da mensagem ao método 
	private Map<Class<?>, Method> mapaMetodos = new HashMap<Class<?>, Method>();
	
	/**
	 * Construtor da mensageria.
	 */
	public ServicoMensageria() {
		inicializaServicoMensageria();
	}
	
	@Override
	public MensagemResponseBase processarMensagem(MensagemRequestBase request) {
		
		// Resposta
		MensagemResponseBase response = new MensagemResponseBase();
		response.setCodigoMensagemRequisicao(request.getCodigoMensagem());
		
		try {
			
			// Mensagem de retorno (especifica ou base)
			MensagemResponseAnotacao msgResponseAnotacao = request.getClass().getAnnotation(
					MensagemResponseAnotacao.class);
			if (msgResponseAnotacao != null) {
				response = (MensagemResponseBase) msgResponseAnotacao.
					classeMensagemResponse().newInstance();
			}
			
			// Valido se existe a mensagem no dicionário
			Class<? extends MensagemRequestBase> classe = request.getClass();
			if (!mapaMetodos.containsKey(classe)) {
				throw new ClassNotFoundException("Class '" + classe.getCanonicalName()
						+ "' is not a known class.");
			}
			// Obtenho o m�todo atrav�s do tipo da mensagem de requisi��o
			Method metodo = mapaMetodos.get(classe);
			// Ativo o servi�o deste m�todo
			Object servico = Ativador.get(metodo.getDeclaringClass());
			// Invoco-o
			response = (MensagemResponseBase) metodo.invoke(servico, request);

		} catch(Exception e) {
			response.setExcecao(e);
		}
		return response;
	}
	
	// Configura o serviço
		private void inicializaServicoMensageria() {
			try {
				// Obtenho a configuração deste serviço
				//ServicoMensageriaConfig config = Configurador.get(ServicoMensageriaConfig.class);
				ResolutorTipo resolutorTipo = ResolutorTipo.getInstance();
				
				// Procurando por classes conhecidas
				for (String nomeCompletoClasse : resolutorTipo.getMapaServicosConhecidos().keySet()) {
					// Descrevo a classe através do seu nome
					Class<?> classe = ResolutorTipo.getService(nomeCompletoClasse);
					// É uma interface?
					if (classe.isInterface()) {
						// Percorro os métodos
						Method[] metodos = classe.getMethods();
						for (Method metodo : metodos) {
							// Quando encontrar um método que recebe SOMENTE um parâmetro do tipo
							// br.com.resource.citibank.marc.comum.mensagem.MensagemRequestBase
							// guardo no dicionário
							Class<?>[] parametros = metodo.getParameterTypes();
							if (parametros.length == 1
									&& MensagemRequestBase.class.isAssignableFrom(parametros[0])) {
								mapaMetodos.put(parametros[0], metodo);
							}
						}
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

}
